use crate::modeling::Value;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[derive(Debug, Deserialize, Serialize)]
pub enum HttpMethod {
    Options,
    Get,
    Post,
    Put,
    Delete,
    Head,
    Trace,
    Connect,
    Patch,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct HttpRequest {
    pub url: String,
    pub method: HttpMethod,
    pub version: String,
    pub headers: HashMap<String, String>,
    pub body: Option<Vec<u8>>,
    pub extensions: HashMap<String, String>,
}

impl TryFrom<String> for HttpMethod {
    type Error = anyhow::Error;

    fn try_from(value: String) -> Result<Self, Self::Error> {
        let m = value.to_lowercase();
        if m == "get" {
            Ok(HttpMethod::Get)
        } else if m == "post" {
            Ok(HttpMethod::Post)
        } else if m == "delete" {
            Ok(HttpMethod::Delete)
        } else if m == "options" {
            Ok(HttpMethod::Options)
        } else if m == "put" {
            Ok(HttpMethod::Put)
        } else {
            Ok(HttpMethod::Get)
        }
    }
}

#[derive(Debug, Deserialize, Serialize)]
pub struct HttpResponse {
    pub status: u16,
    pub headers: HashMap<String, String>,
    pub body: Vec<u8>,
}
// Http09 => "HTTP/0.9",
// Http10 => "HTTP/1.0",
// Http11 => "HTTP/1.1",
// H2 => "HTTP/2.0",
// H3 => "HTTP/3.0",
impl HttpRequest {
    pub fn get(url: String) -> anyhow::Result<Self> {
        Ok(Self {
            url,
            method: HttpMethod::Get,
            version: String::from("HTTP/1.1"),
            headers: HashMap::new(),
            body: None,
            extensions: HashMap::new(),
        })
    }
    pub fn post(
        url: String,
        headers: HashMap<String, String>,
        body: Option<Vec<u8>>,
    ) -> anyhow::Result<Self> {
        Ok(Self {
            url: url,
            method: HttpMethod::Post,
            version: String::from("HTTP/1.1"),
            headers: headers,
            body: body,
            extensions: HashMap::new(),
        })
    }
}

impl TryFrom<Value> for HttpRequest {
    type Error = anyhow::Error;

    fn try_from(value: Value) -> Result<Self, Self::Error> {
        let serde_value: serde_json::Value = value.try_into()?;
        let message = serde_json::from_value(serde_value)?;
        Ok(message)
    }
}

impl TryInto<Value> for HttpRequest {
    type Error = anyhow::Error;

    fn try_into(self) -> Result<Value, Self::Error> {
        Value::try_from(serde_json::json!(self))
    }
}
